With apps getting more complex than ever, it’s important to test them automatically. We can do this with unit tests, and then we don’t have to test everything by hand.
In this article, we’ll look at how to test Vue 3 apps by writing a simple app and testing it.
Testing With a Real Router
We can write tests that test our app with the real router.
For example, we can write:
src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import Edit from '../views/Edit.vue'
import Post from '../views/Post.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/edit',
name: 'edit',
component: Edit
},
{
path: '/posts/:id/edit',
name: 'post',
component: Post
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
src/views/Edit.vue
<template>
<button @click="redirect">Click to Edit</button>
</template>
<script>
export default {
methods: {
redirect() {
this.$router.push(`/posts/1/edit`);
},
},
};
</script>
src/views/Post.vue
<template>
<div>Post {{$route.params.id}}</div>
</template>
tests/unit/example.spec.js
import { flushPromises, mount } from '@vue/test-utils'
import App from '@/App'
import router from '@/router'
describe('component handles routing correctly', () => {
it('allows authenticated user to edit a post', async () => {
router.push('/edit')
await router.isReady()
const wrapper = mount(App, {
global: {
plugins: [router],
}
})
await wrapper.find('button').trigger('click')
await flushPromises()
expect(wrapper.html()).toContain('Post 1')
})
})
We have some routes mapped to some components.
Then in our test, we redirect to the edit
route so the Edit
component is loaded.
Next, we call router.isReady()
to wait until the Vue Router is loaded.
Then we mount the App
and pass in the router
into the plugins
property.
Then we trigger a click on the button in the Edit
component.
And finally, we check what’s rendered after calling flushPromises
to wait until the new route is loaded.
Stubbing a Single Child Component
We can stub child components in our app.
For example, we can write:
import { mount } from '@vue/test-utils'
import axios from 'axios';
const FetchDataFromApi = {
name: 'FetchDataFromApi',
template: `
<div>{{ result }}</div>
`,
async mounted() {
const res = await axios.get('https://yesno.wtf/api')
this.result = res.data
},
data() {
return {
result: ''
}
}
}
const App = {
components: {
FetchDataFromApi
},
template: `
<div>
<h1>Welcome to Vue.js 3</h1>
<fetch-data-from-api />
</div>
`
}
test('stubs component with custom template', () => {
const wrapper = mount(App, {
global: {
stubs: {
FetchDataFromApi: {
template: '<span />'
}
}
}
})
expect(wrapper.html()).toContain('Welcome to Vue.js 3')
})
We have the FetchDataFromApi
component that we want to ignore in our test.
It’s used in App
, but we don’t want to mount it in our test.
Therefore, we want to stub this component. To do this, we pass in a stubbed component into the global.stubs
property.
Then we check the content of App
in the last line of the test.
Conclusion
We can test our components with a real Vue Router with Vue 3 components.
Also, we can stub components and test only the parts we want to test.